package com.sage.logbook.server;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.jetty.proxy.ProxyServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;

import com.sage.logbook.config.ServerConfig;

/**
 * プロキシサーバーです
 *
 */
public final class ProxyServer extends Thread {

    private static final Logger LOGGER = LogManager.getLogger(ProxyServer.class);

    private static final ProxyServer SERVER_INSTANCE = new ProxyServer();

    private Server server = null;

    /** ローカルループバックアドレスからの接続のみ受け入れる */
    private final boolean allowOnlyFromLocalhost = ServerConfig.isAllowOnlyFromLocalhost();

    private final int port = ServerConfig.getListenPort();

    private ProxyServer() {
        this.setName("logbook_proxy_server");
    }

    @Override
    public void run() {

        this.initAll();

        try {
            this.server.start();
            this.server.join();
        } catch (Exception e) {
            LOGGER.fatal("サーバーの起動に失敗しました", e);
        }
    }

    private void initAll() {
        this.server = initServer(this.allowOnlyFromLocalhost, this.port);

        ServletHandler servletHandler = initSevletHandler();

        servletHandler.setServer(this.server);
        this.server.setHandler(servletHandler);
    }

    private Server initServer(boolean allowOnlyFromLocalhost, int port) {
        if (allowOnlyFromLocalhost) {
            // 检查port参数合法性
            InetSocketAddress inetSocketAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
            return new Server(inetSocketAddr);
        } else {
            return new Server(port);
        }
    }

    private ServletHandler initSevletHandler() {
        Map<String, String> common = new HashMap<String, String>(2);
        common.put("maxThreads", "32");
        common.put("timeout", "300000");

        ServletHolder reverseHolder = new ServletHolder(ReverseProxyServlet.class);
        reverseHolder.setInitParameters(common);

        ServletHolder servletHolder = new ServletHolder(ProxyServlet.class);
        servletHolder.setInitParameters(common);

        ServletHandler servletHandler = new ServletHandler();
        servletHandler.addServletWithMapping(servletHolder, "/*");
        servletHandler.addServletWithMapping(reverseHolder, "/kcsapi/*");

        return servletHandler;
    }

    private void shutdown() {
        if (this.server == null) {
            return;
        }
        try {
            this.server.stop();
        } catch (Exception e) {
            LOGGER.fatal("サーバーの停止に失敗しました", e);
        } finally {
            this.server = null;
        }
    }

    public static ProxyServer getInstance() {
        return SERVER_INSTANCE;
    }

    public static void end() {
        SERVER_INSTANCE.shutdown();
    }
}
